package js;
import data.ShopLoader;
import haxe.Http;
import haxe.Template;
import js.Dom;
import data.HxTemplate;
import data.Item;
using StringTools;
using me.cunity.tools.ArrayTools;
using me.cunity.tools.MathTools;
/**
 * ...
 * @author Axel Huizinga axel@cunity.me
 */

typedef CartData =
{
	//public var fieldNames:Array<String>;
	//public var formFields:Array<Dynamic>;
}

typedef Address = 
{ //>CartData,
	public var label:String;
	public var name:String;
	public var type:String;
	public var road:Dynamic;
	public var zip:Dynamic;
	public var location:Dynamic;
	public var phone:Dynamic;
}
 
typedef Customer = 
{>CartData,
	public var firstName:Dynamic;
	public var surname:Dynamic;
	public var name:Dynamic;
	public var email:Dynamic;
	public var address:Address;
}

typedef Order = 
{>CartData,
	public var deliveryAddress:Address;
	public var date:Dynamic;
	public var name:Dynamic;
	public var items:Array<Item>;
	public var shipping:Dynamic;
	public var total:Dynamic;
	public var vat:Dynamic;
}

typedef ShoppingSession =
{
	public var date:Dynamic;
	public var ip:Dynamic;
	public var lastVisit:Dynamic;
}
 
class Cart 
{
	public var cookiesEnabled:Bool;
	var win:Window;
	public var items:Hash<Item>;
	var contentBox:HtmlDom;
	var complete:Bool;
	var customer:Customer;
	var order:Order;
	var req:XMLHttpRequest;
	var session:ShoppingSession;
	var shopPage:ShopPage;
	//var orderItems:Hash<Item>;
	var customerFields:Hash<Hash<String>>;
	var addressFields:Hash<Hash<String>>;
	var deliveryAddressFields:Hash<Hash<String>>;
	var itemFields:Hash<Hash<String>>;
	var orderFields:Hash<Hash<String>>;
	var t:HxTemplate;
	

	public function new(win:Window) 
	{
		this.win = win;
		var cookie:String = win.document.cookie;
		trace(cookie);
		cookiesEnabled = cookie != '';
		items = new Hash();
		t = new HxTemplate(haxe.Resource.getString("shopNav"));
		complete = true;
	}
	
	public function init(x:Xml)
	{
		//Lib.alert('ii');
		xml2cart(x.firstElement());
		//trace(Std.string(customer));
		//trace(Std.string(order));
		//trace(Std.string(session));
		if (getCookie('cellamella') == null)
			createCookie('cellamella', 'shop', 100);
		else
		{
			//TODO: COMPLETE COOKIE IMPLEMENTATION
		}
		
		if (customer.surname.value == '')
			customer.surname.value= 'Gast';
		win.add2cart = add;
		win.removeAll = removeAll;
		win.removeFromCart = remove;
		win.loadDeliveryAddressForm = loadDeliveryAddressForm;
		win.processOrder = processOrder;
		win.cart = this;
		trace(win.location.href + ':' + win.add2cart);
	}
	
	public function updateCart(sPage:ShopPage=null)
	{
		var numItems:Int = order.items.length;
		if (sPage != null)
			shopPage = sPage;
		var data:Dynamic = 
		{ 
			date:new DateFormat().text,
			customer:Std.string(customer.firstName.value) + Std.string(customer.surname.value),
			numItems:numItems,
			order:order
		};
		var shopNav:HtmlDom = win.document.getElementById('shopNav');
		//trace(Std.string(data));
		//
		trace('');
		var update:String = t.execute(data, {addListItems:addListItems});
		shopNav.innerHTML = update;
		//shopNav.innerHTML = t.execute(data, {addListItems:addListItems});
	}
	
	public function checkout( ?coBox:HtmlDom)
	{
		if (coBox != null)
			contentBox = coBox;
		var c:HxTemplate = new HxTemplate(haxe.Resource.getString("checkout"));
		//var c:Template = new Template(haxe.Resource.getString("checkout"));
		if (customer.surname.value == 'Gast')
			customer.surname.value = '';

		var checkoutFields:Array<Dynamic> = new Array();
		for (f in Reflect.fields(customer))
		{
			switch (f )
			{
				case 'address':
				var field:Dynamic = Reflect.field(customer, f);
				checkoutFields.push( { name:'address', label:field.label, type:field.type} );
				//trace('address fields.length:'  + Reflect.fields(field).length);
				for (ff in Reflect.fields(field))
				if(!Std.is(Reflect.field(field, ff), String))
					checkoutFields.push(Reflect.field(field, ff)); 
				continue;
				//case 'surname':
				
				default:
				checkoutFields.push(Reflect.field(customer, f));
			}			
		}

		//trace(Std.string({date:new DateFormat().text, checkout:checkoutFields}));
		//trace(c.execute({date:new DateFormat().text, fields:Reflect.fields(customer), form:customer.firstName}));
		var res:String = c.execute( { date:new DateFormat().text, error:!complete, checkout:checkoutFields }, {addListItems:addListItems} );
		/*var mainFrame:HtmlDom = win.document.getElementsByName('mainFrame')[0];
		trace(mainFrame.getAttribute('src'));
		mainFrame.setAttribute('src', 'form.html'); return;*/
		contentBox.innerHTML = res;
	}
	
	public function loadDeliveryAddressForm(box:HtmlDom)
	{
		var fields:Array<Dynamic> = new Array();
		var c:HxTemplate = new HxTemplate('<table >
				::foreach key checkout::
				::if (key.name == "deliveryAddress")::
				<tr>
					<td colspan="2" style="text-align:left;margin: 20px;">::key.label::</td>
				</tr>
				::else::
				<tr>
					<td class="right"><input type="::key.type::" size="::key.size::" value="::key.value::" name="::key.name::" /></td>
					<td>::key.label:: ::if (key.required == "true")::*::end::  </td>
				</tr>
				::end::
				::end::
				</table>	');
		for (f in Reflect.fields(order))
		{
			switch (f )
			{
				case 'deliveryAddress':
				fields.push( { name:'deliveryAddress', label:order.deliveryAddress.label, type:order.deliveryAddress.type} );
				//trace('deliveryAddress fields.length:'  + Reflect.fields(field).length);
				for (ff in Reflect.fields(order.deliveryAddress))
				if(!Std.is(Reflect.field(order.deliveryAddress, ff), String))
					fields.push(Reflect.field(order.deliveryAddress, ff)); 
				var res:String = c.execute( { date:new DateFormat().text, checkout:fields } );
				box.innerHTML = res;
				box.style.display = 'block';
			}			
		}		
	}
	
	//public function processOrder(f:Form, doc:Document)
	public function processOrder(f:Form, w:Window)
	{
		var doc:Document = w.document;
		/*for (el in 0...f.elements.length)
		{
			trace(f.elements[el].name +':' + f.elements[el].value);
		}*/
		//var customerData:Hash<String> = new Hash();
		trace('order.total.value:' + order.total.value);
		var value:String = '';
		complete = true;
		for (name in Reflect.fields(customer))
		{
			var obj:Dynamic = Reflect.field(customer, name);
			trace(name + ':' + Std.is(obj, String));
			if (obj.type == null)//NO INPUT
				continue;
			if (!Std.is(obj, String) )
			{				
				value = untyped f.elements[obj.name].value;
				if ( obj.required == 'true' && value == '')
				{
					trace(name + ':' + obj.required);
					complete = false;
				}
				if(value !='')
				{
					Reflect.setField(obj, 'value', value);					
					//customerData.set(obj.label , value);					
					trace(obj.label +' set2:' + value + ' -> ' + Reflect.field(obj, name));
				}
				customerFields.get(name).set('value' , value);
			}
					
		}		
		//trace(complete + Std.string(customer));
		for (name in Reflect.fields(customer.address))
		{
			var obj:Dynamic = Reflect.field(customer.address, name);
			trace(name + ':' + Std.is(obj, String));
			if (obj.type == null)//NO INPUT
				continue;
			if (!Std.is(obj, String) )
			{				
				value = untyped f.elements[obj.name].value;
				if ( obj.required == 'true' && value == '')
				{
					trace(name + ':' + obj.required);
					complete = false;
				}
				if(value !='')
				{
					Reflect.setField(obj, 'value', value);		
					//customerData.set(obj.label , value);
					//trace(obj.label +':' + value);
				}
				addressFields.get(name).set( 'value', value);		
			}					
		}
		
		//trace(complete);
		var deliveryAddressData:Hash<String> = new Hash();		
		if (untyped f.elements['useDeliveryAddress'].value == 'true')
		{
			deliveryAddressData.set(order.deliveryAddress.label, order.deliveryAddress.label);
			for (name in Reflect.fields(order.deliveryAddress))
			{
				var obj:Dynamic = Reflect.field(order.deliveryAddress, name);
				trace(name + ':' + Std.is(obj, String));
				if (obj.type == null)//NO INPUT
					continue;
				if (!Std.is(obj, String) )
				{				
					value = untyped f.elements[obj.name].value;
					if ( obj.required == 'true' && value == '')
					{
						trace(name + ':' + obj.required);
						complete = false;
					}
					if(value !='')
					{
						Reflect.setField(obj, 'value', value);
						deliveryAddressData.set(obj.label , value);
						//trace(obj.label +':' + value);
					}						
				}
				deliveryAddressFields.get(name).set( 'value', value);
			}			
		}
		if(complete)
			complete = (untyped f.elements['agb']).checked;
		//complete &= cast(untyped f.elements['agb'], Checkbox).checked;
		trace('agb:' + (untyped f.elements['agb']).checked + ' complete:' + complete);
		if (!complete)
		{
			w.document.getElementById('errorBox').style.display = 'block';
			return;
		}
		//trace(complete); return;
		//EVERYTHING OK NOW - SUBMIT ORDER DATA
		order.date.value = untyped f.elements['date'].value;
		
		f.action = 'bestellung.php';
		//f.action = 'tcpdf/devel/order.php';
		//f.appendChild(createHidden(order.htmlList.label, doc.getElementById('orderList').innerHTML, doc));	
		var fH:Hash<String> = null;
		//f.appendChild(createHidden('auth', 'cellamella666', doc));
		for (name in customerFields.keys())
		{
			fH = customerFields.get(name);
			for (fName in fH.keys())
				f.appendChild(createHidden('customer['+name+'][' + fName+ ']', fH.get(fName), doc));
			
		}
		for (name in addressFields.keys())
		{
			fH = addressFields.get(name);
			for (fName in fH.keys())
				f.appendChild(createHidden('address['+name+'][' + fName+ ']', fH.get(fName), doc));
			
		}
		
		if (untyped f.elements['useDeliveryAddress'].value == 'true')
		for (name in deliveryAddressFields.keys())
		{
			fH = deliveryAddressFields.get(name);
			for (fName in fH.keys())
				f.appendChild(createHidden('deliveryAddress['+name.replace('d_', '')+'][' + fName+ ']', fH.get(fName), doc));
			
		}	
		for(name in itemFields.keys())
		{
			/*var itemData:String = item.count +'|' + item.unit +  '|' + item.title + ' '  + item.volume + item.volumeUnit;
			itemData += '|' + item.id + '|' + shopPage.float2String(item.price * item.count) + ' ' +item.currencySymbol;
			itemData += '|' + item.headline;*/
			fH = itemFields.get(name);
			for(fName in fH.keys())
				f.appendChild(createHidden('itemField[' + name +'][' + fName + ']', fH.get(fName) , doc));
		}	
		
		var vatValues:Hash<Array<Item>> = new Hash();
		var itemIndex:Int = 0;
		for (item  in order.items)
		{
			f.appendChild(createHidden('item[' + itemIndex + '][count]', Std.string(item.count), doc));
			f.appendChild(createHidden('item[' + itemIndex + '][packaging]', item.packaging, doc));
			f.appendChild(createHidden('item[' + itemIndex + '][title]', item.title, doc));
			f.appendChild(createHidden('item[' + itemIndex + '][volume]', Std.string(item.volume), doc));
			f.appendChild(createHidden('item[' + itemIndex + '][volumeUnit]', item.volumeUnit, doc));
			f.appendChild(createHidden('item[' + itemIndex + '][headline]', item.headline, doc));
			f.appendChild(createHidden('item[' + itemIndex + '][id]', item.id, doc));
			f.appendChild(createHidden('item[' + itemIndex + '][vat]', Std.string(item.vat), doc));
			f.appendChild(createHidden('item[' + itemIndex + '][price]', Std.string(item.price), doc));
			f.appendChild(createHidden('item[' + itemIndex + '][sum]', Std.string(item.price*item.count), doc));

			if (!vatValues.exists( Std.string(item.vat))) 
				vatValues.set(Std.string(item.vat), [item]);
			
			else
				vatValues.get(Std.string(item.vat)).push(item);			
			
			itemIndex++;
		}
		
		for (name in orderFields.keys())
		{
			if (name == 'items')
				continue;
			fH = orderFields.get(name);
			trace(name + ':' + Std.string(fH));
			for (fName in fH.keys())
			{
				trace('order[' + name +'][' + fName + ']' + ':' + fH.get(fName));
				f.appendChild(createHidden('order[' + name +'][' + fName + ']', fH.get(fName), doc));
			}
				
			f.appendChild(createHidden('order[date][value]', order.date.value, doc));
			//f.appendChild(createHidden('order[vat][value]', order.vat.value, doc));
		}
		//ADD VATSUMS SORTED AT VATRATE
		var rowPrice:Float;		
		for (k in vatValues.keys())
		{
			var vatVal:Float = 0;
			var vatObj:Array<Item> = vatValues.get(Std.string(k));
			for (item in vatObj)
			{
				rowPrice = item.price * item.count;
				vatVal += (rowPrice - (rowPrice * 100 / (100 + item.vat)));
			}
			
			f.appendChild(createHidden('order[vatSum][' + k +']' ,  Std.string(vatVal.round2()), doc));		
		}

		f.submit();
		//w.location.replace("Danke.php?email=" + customer.email.value);
	}
	
	function createHidden(name:String, data:String, doc:Document):HtmlDom
	{
		var hidden:HtmlDom = doc.createElement('input');
		hidden.setAttribute('type', 'hidden');
		hidden.setAttribute('name', name);
		hidden.setAttribute('value', data);
		return hidden;
	}
	
	public function createCookie(name:String, value:String, days:Int = 0) 
	{ 
		var expires = ""; 
		if (days >= 0) 
		{ 
			var date = Date.fromTime(Date.now().getTime()  + (days * 24 * 60 * 60 * 1000)); 
			expires = "; expires=" + date.toString(); 
		} 		
		win.document.cookie = name + "=" + value + expires + "; path=/"; 

	}

	function eraseCookie(name:String) 
	{ 
		createCookie(name, "", -1);
	}
	
	function getCookie(name:String):String
	{
		var data:Array<String> = win.document.cookie.split(';');
		for (c in data)
		{
			var pair:Array<String> = c.trim().split('=');
			if (pair.length == 2 && pair[0] == name)
				return pair[1];
		}
		return null;
	}
	
	function restoreSession()
	{
		
	}
	
	public function remove(id:String, doc:Document):Bool
	{
		var f:Form = untyped doc.forms[id];
		var count:Int = untyped f.elements['count'].value *-1;
		
		if (!changeItem(id, count))
		{			
			return false;
		}
		
		updateCart();
		return false;
	}
	
	function removeAll(id:String):Bool
	{
		var item:Item = items.get(id);
		changeItem(id, item.count * -1);
		updateCart();
		trace('win.sWin.document.title:' + win.sWin.document.title) ;
		if (win.sWin.document.title == 'Kasse') 
		{
			//trace (order.total.value + ' == ' + order.shipping.value);
			//return false;
			if (order.items.length == 0)
			win.sWin.location.href = 'ShopContent.html';
			else
			win.sWin.location.reload();
			//win.sWin.document.getElementById('orderList').innerHTML =  updateListItems('checkout');
			//Lib.alert(win.sWin.document.getElementById('orderList').innerHTML);			
		}

		return false;
	}
	
	public function add(id:String, doc:Document):Bool
	{
		var f:Form = untyped doc.forms[id];
		var count:Int = untyped f.elements['count'].value;
		
		if (!changeItem(id, count))
		{			
			var item:Item = items.get(id);
			item.count = count;
			order.items.push(item);
		}
		
		updateCart();
		return false;
	}
	
	function changeItem(id:String, count:Int):Bool
	{	
		for (i in order.items)
			if (i.id == id)
			{
				var add:Bool = count > 0;
				if (!add)
					count *= -1;
				var current:Int = i.count;
				for (a in 0...count)
					add ? current++ : current--;
				i.count = current;
				trace(i.count);
				if (i.count < 0)
					i.count = 0;
				if (i.count == 0)
				order.items.remove(i);
				return true;
			}
		return false;
	}
	
	function addListItems(resolve:String->Dynamic, s:String):String
	{
		return updateListItems(s);
	}
	
	function updateListItems(s:String):String
	{	
		trace(shopPage + '->'+s+'<-' + ':'+ (s == "checkout" ));
		if (shopPage == null)
			return '';
		var content:String = '';
		var total:Float = 0;
		var currencySymbol:String = shopPage.currencySymbol;
		//TODO: HANDLE DIFFERENT VAT RATES
		//trace(order.items.length + ':' + Std.string(order.items[0]));
		var vatValues:Hash<Array<Item>> = new Hash();
		var rowPrice:Float;
		for (item  in order.items)
		{
			content += '<tr>';
			rowPrice = item.price * item.count;
			total += rowPrice;
			trace(total+ ':' + s);
			if (s == 'checkout')
				content += '<td class="right" >' + item.count +' ' + item.packaging +  '</td><td  >' + item.title + ' '  + item.volume + item.volumeUnit +
				'</td><td  class="itemHeadline">' +item.headline ;
			else
				content += '<td class="right"><a href="#" onclick="removeAll(\'' +item.id +
				'\')" ><img src="html/img/pool/icons/cross.png" style="float:left" title="Artikel löschen"/></a>' + item.count + '</td><td title="' + item.volume + item.volumeUnit + '">'  + item.title;
			//if (s == 'checkout' ) content += ' ' + item.volume + item.volumeUnit;
			content +=   '</td><td class="right">' + shopPage.float2String(rowPrice) + ' ' +item.currencySymbol + '</td>';
			content += '</tr>';
			if (!vatValues.exists( Std.string(item.vat))) 
				vatValues.set(Std.string(item.vat), [item]);
			
			else
				vatValues.get(Std.string(item.vat)).push(item);
				
		}
		//trace(Std.string(order));
		//trace(Std.string(order.shipping));
		var freeMin:Float = Std.parseFloat(order.shipping.freeMin);
		//trace('freeMin:' + freeMin + ((freeMin > total)));
		
		var cSpan:String = (s == 'checkout') ? '3' : '2';
		
		if (freeMin > total)
		{
		content += '<tr>';
		content += '<td  colspan="'+cSpan+'">' + order.shipping.label + '</td>';
		content += '<td class="right">' + shopPage.float2String(order.shipping.value) + ' ' + currencySymbol + '</td>';
		content += '</tr>';						
		total += Std.parseFloat(order.shipping.value);
		}
		if (s == "checkout" )
		{
			order.total.value = total;
			orderFields.get('total').set('value', Std.string(total));
			//}
			trace('order.total.value:' + order.total.value + ':' + total);			
		}

		/*var keys:Array<String> = vatValues.keys().stringIt2Array(true);
		trace(keys);
		keys.sort(function(a, b) return Reflect.compare(Std.parseFloat(a), Std.parseFloat(b)) ); */
		for (k in vatValues.keys())
		{
			var vatVal:Float = 0;
			var vatObj:Array<Item> = vatValues.get(Std.string(k));
			for (item in vatObj)
			{
				rowPrice = item.price * item.count;
				vatVal += (rowPrice - (rowPrice * 100 / (100 + item.vat)));
			}
			
			//trace(k +'=>' + vatObj.value +':' + Type.typeof(vatObj.value) +':' + Std.is(vatObj.value, String));
			content += '<tr>';
			content += '<td  colspan="'+cSpan+'">Enthält ' + k + '% ' + shopPage.vatName + '</td>';
			content += '<td class="right">' + shopPage.float2String(vatVal.round2())+ ' '  + currencySymbol +'</td>';
			content += '</tr>';		
		}
		content += '<tr>';
		content += '<td class="sum" colspan="'+cSpan+'">Summe</td>';
		content += '<td class="right sum">' + shopPage.float2String(total) + ' ' + currencySymbol + '</td>';
		content += '</tr>';			
		
		return content;
	}
	
	public function saveSession(evt:Event)
	{
		
	}
	
	public function xml2cart(x:Xml):Dynamic
	{
		customer = cast { };
		//customer.formFields = new Array();
		order = cast {};
		//order.formFields = new Array();
		session = cast { };
		addressFields = new Hash();
		customerFields = new Hash();
		var cX:Xml =  x.elementsNamed('customer').next();
		var address:Address = cast { };
		//address.formFields = new Array();
		for (c in cX.elements())
		{
			customerFields.set(c.nodeName, attributes2hash(c));
			switch (c.nodeName)
			{
				case 'address':
				attributes2object(c, address);
				//addressFields.set(c.nodeName, attributes2hash(c));
				for (a in c.elements())
				{
					addressFields.set(a.nodeName, attributes2hash(a));
					Reflect.setField(address, a.nodeName, attributes2object(a));
				}
				customer.address = address;
				default:
				Reflect.setField(customer, c.nodeName, attributes2object(c));
			}
		}
		
		var sX:Xml = x.elementsNamed('shoppingSession').next();
		for (s in sX.elements())
			Reflect.setField(session, s.nodeName, attributes2object(s));
		
		//Lib.alert('going2init order');
		deliveryAddressFields = new Hash();
		orderFields = new Hash();
		var address:Address = cast { };	
		var oX:Xml = x.elementsNamed('order').next();
		for (o in oX.elements())
		{
			orderFields.set(o.nodeName, attributes2hash(o));
			switch(o.nodeName)
			{
				case 'deliveryAddress':
				attributes2object(o, address);
				//deliveryAddressFields.set(o.nodeName, attributes2hash(o));
				for (a in o.elements())
				{
					deliveryAddressFields.set(a.nodeName, attributes2hash(a));
					Reflect.setField(address, a.nodeName, attributes2object(a));
				}
				order.deliveryAddress = address;
				
				case 'items':
				order.items = new Array();
				//Lib.alert('order.items:' + order.items);
				default:
				Reflect.setField(order, o.nodeName, attributes2object(o));
			}
		}		
		
		itemFields = new Hash();
		var itemsX:Xml = x.elementsNamed('itemFields').next();
		for (i in itemsX.elements())
		{
			itemFields.set(i.nodeName, attributes2hash(i));			
		}
		
		return order;
	}
	
	function attributes2hash(x:Xml):Hash<String>
	{
		var h:Hash<String> = new Hash();
		//h.set('value', '');
		for (a in x.attributes())
			h.set(a, x.get(a));
		return h;
	}
	
	function attributes2object(x:Xml, o:Dynamic=null):Dynamic
	{
		if (o == null)
			o = { };
		try
		{
			o.value = '';				
			o.name = x.nodeName;
			for (a in x.attributes()) 
				Reflect.setField(o, a, x.get(a));		
		}
		catch (ex:Dynamic)
		{
			trace(ex);
		}
		return o;
	}
}